package cn.shop.merchant.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.shop.merchant.mapper.gametemplate.GamePartitionMapper;
import cn.shop.merchant.mapper.player.PlayerAccountMapper;
import cn.shop.merchant.mapper.user.MerchantAliasMapper;
import cn.shop.merchant.model.gameconfig.GameInfo;
import cn.shop.merchant.model.gameconfig.GamePartition;
import cn.shop.merchant.model.gameconfig.Npc;
import cn.shop.merchant.model.user.MerchantAlias;
import cn.shop.merchant.req.GamePartitionQueryReq;
import cn.shop.merchant.service.message.MessageUtil;
import cn.shop.msgs.PartitionDeleteMsg;
import cn.shop.transcation.order.entity.Order;
import cn.shop.transcation.order.mapper.OrderMapper;
import cn.shop.utils.PageUtil;
import cn.shop.utils.Putif;
import cn.shop.utils.enums.OrderStatus;
import cn.shop.utils.exception.ServiceException;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toList;

/**
 * @author : 李一涵
 * @date : Created in 2021-11-1 16:11
 * @description : 游戏分区配置service
 */
@Service
@SuppressWarnings("unused")
public class GamePartitionService extends BaseServiceImpl<GamePartitionMapper, GamePartition>{

    private final Logger LOG = LoggerFactory.getLogger(GamePartition.class);

    @Resource
    private NpcService npcService;
    @Resource
    private GameInfoService gameInfoService;
    @Resource
    private GameTemplateService templateService;
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private PlayerAccountMapper playerAccountMapper;
    @Resource
    private MerchantAliasMapper aliasMapper;
    @Resource
    private MessageUtil messageUtil;

    @Transactional(rollbackFor = Exception.class)
    public boolean insertGamePartition(GamePartition partition) {
        List<Npc> npcs = partition.getNpcs();
        if (CollectionUtil.isEmpty(npcs)) {
            return false;
        }
        GameInfo game = gameInfoService.getById(partition.getGameId());
        partition.setTemplateId(game.getTemplateId());
        boolean isSuccess = save(partition);
        if (!isSuccess) {
            return false;
        }
        npcs.forEach(npc -> npc.setPartitionId(partition.getId()));
        return npcService.saveBatch(npcs);
    }

    /**
     * 更新游戏分区信息
     * @param partition 游戏分区对象
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean updateGameTemplate(GamePartition partition, long uid) {
        npcService.remove(Wrappers.<Npc>lambdaQuery().eq(Npc::getPartitionId, partition.getId()));
        List<Npc> npcs = partition.getNpcs();
        npcs.forEach(npc -> npc.setPartitionId(partition.getId()));
        npcService.saveBatch(npcs);
        GameInfo game = gameInfoService.getById(partition.getGameId());
        partition.setTemplateId(game.getTemplateId());
        return update(partition, Wrappers.<GamePartition>lambdaQuery()
                .eq(GamePartition::getUid, uid)
                .eq(GamePartition::getId, partition.getId()));
    }

    /**
     * 获取指定uid的分页列表
     *
     * @param uid 商户编号
     * @param req 查询条件
     */
    public Map<String, Object> findConfigList(long uid, GamePartitionQueryReq req) {
        req.setUid(uid);
        Page<GamePartition> page = Page.of(req.getCurrent(), req.getSize());
        page = baseMapper.partitionList(page, req);
        return PageUtil.formatPage(page, this::format);
    }

    /**
     * 删除指定uid和id的记录
     * @param id 记录编号
     * @param uid 商户编号
     * @return 是否删除成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(long id, long uid) {
        npcService.remove(Wrappers.<Npc>lambdaQuery().eq(Npc::getPartitionId, id));
        playerAccountMapper.deleteBind(id, LocalDateTime.now());
        return SqlHelper.retBool(getBaseMapper().batchDeleteWithFill(new GamePartition(), Wrappers.<GamePartition>lambdaQuery()
                .eq(GamePartition::getId, id)
                .eq(GamePartition::getUid, uid)));
    }

    public boolean transfer(List<Long> from, long to) {
        GamePartition partition = getById(to);
        if (partition == null) {
            return false;
        }
        LambdaUpdateWrapper<Order> update = Wrappers.lambdaUpdate();
        update.set(Order::getPartitionId, to)
                .in(Order::getStatus, Arrays.asList(OrderStatus.AUDITING, OrderStatus.PASSED))
                .set(Order::getTemplateId, partition.getTemplateId())
                .in(Order::getPartitionId, from);
        return SqlHelper.retBool(orderMapper.update(null, update));
    }

    /**
     * 获取指定id和uid的记录
     * @param id 模板编号
     * @param uid 商户编号
     * @return 查询的信息
     */
    public Map<String, Object> getInfo(long id, long uid) {
        GamePartition partition = getOne(Wrappers.<GamePartition>lambdaQuery()
                .eq(GamePartition::getId, id)
                .eq(GamePartition::getUid, uid));
        List<Npc> npcs = npcService.list(Wrappers.<Npc>lambdaQuery().eq(Npc::getPartitionId, id));
        Map<String, Object> map = format(partition);
        map.put("ip", partition.getIp());
        map.put("port", partition.getPort());
        map.put("serverFolder", partition.getServerFolder());
        map.put("gameId", partition.getGameId());
        map.put("npcs", npcs.stream().map(this::format).collect(toList()));
        map.put("createScript", partition.getCreateScript());
        return map;
    }

    public void deleteSendToGateway(GamePartition partition, long uid) throws Exception {
        messageUtil.sendMessage(PartitionDeleteMsg.TOPIC, PartitionDeleteMsg.TAG, PartitionDeleteMsg.start()
                .uid(uid)
                .id(partition.getId())
                .ip(partition.getIp())
                .port(partition.getPort())
                .time(LocalDateTime.now()));
    }

    public long countByGameId(long gameId) {
        return count(Wrappers.<GamePartition>lambdaQuery().eq(GamePartition::getGameId, gameId));
    }

    public List<Map<String, Object>> allListByGameId(long gameId) {
        List<GamePartition> list = list(Wrappers.<GamePartition>lambdaQuery().eq(GamePartition::getGameId, gameId));
        return list.stream()
                .map(g -> Putif.start()
                        .anyway("value", g.getId())
                        .anyway("label", g.getName())
                        .get())
                .collect(toList());
    }

    public List<Map<String, Object>> allListByGame(long uid) {
        List<GamePartition> list = list(Wrappers.<GamePartition>lambdaQuery().eq(GamePartition::getUid, uid));
        return list.stream()
                .map(g -> Putif.start()
                        .anyway("value", g.getId())
                        .anyway("label", g.getName())
                        .get())
                .collect(toList());
    }

    public String getAliasByGameId(long gameId) {
        GameInfo game = gameInfoService.getById(gameId);
        MerchantAlias alias = aliasMapper.selectById(game.getAliasId());
        if (alias == null) {
            throw new ServiceException("未查到二级域名");
        }
        return alias.getAlias();
    }

    /**
     * 格式化数据的方法
     * @param partition 要格式化的数据
     * @return 格式化以后的数据
     */
    private Map<String, Object> format(GamePartition partition) {
        return Putif.start()
                .anyway("id", partition.getId())
                .anyway("game", partition.getGameName())
                .anyway("serverFolder", partition.getServerFolder())
                .anyway("ip", partition.getIp())
                .anyway("template", partition.getTemplateName())
                .anyway("name", partition.getName())
                .anyway("status", partition.getStatus())
                .anyway("level", partition.getLevel())
                .get();
    }

    private Map<String, Object> format(Npc npc) {
        return Putif.start()
                .anyway("name", npc.getName())
                .anyway("mapCode", npc.getMapCode())
                .anyway("positionX", npc.getPositionX())
                .anyway("positionY", npc.getPositionY())
                .anyway("styleCode", npc.getStyleCode())
                .get();
    }
}